/* eslint-disable @typescript-eslint/no-explicit-any */
import { sys } from 'cc';
import { BaseItem, ItemList, LimitType, LimitValue, Tables, TB } from '../../Data/Types';
import { StringUtil } from '../../Util/StringUtil';
import { _classExample, Singleton } from './BaseManager';
import { PlayerData } from '../../Game/Config/PlayerData';
import { DataTempParams } from '../Config/DataConfig';
// import { GameData } from '../../Game/Config/GameData';

/** 数据管理器 */
export class DataManager extends Singleton {
  className: string = 'DataManager';
  /** 表格数据 */
  tables: Tables = null!;
  /** 临时数据 */
  tempSaveData: Map<DataTempParams, any> = new Map();

  /** 玩家数据 */
  player: PlayerData = new PlayerData();

  /** 游戏数据 */
  // games: GameData = new GameData();

  initFirst() {
    this.player.initFirst();
  }

  init() {
    this.player.init();
  }

  resetData() {
    this.player = new PlayerData();
    this.player.Save();
    // this.player.initFirst();
  }

  getTableByString<T>(key: string): T {
    return this.tables[key] as T;
  }

  // get constParam(): TB.Param {
  //   return DataMgr.tables.TbParam.get(1);
  // }

  getTableArr(tableName: string): any[][] {
    return this.tables[tableName] as any[][];
  }
}

// eslint-disable-next-line @typescript-eslint/no-namespace, @typescript-eslint/prefer-namespace-keyword
export namespace Data {
  /** 获取奖励数据 */
  export function getRewardData(list: ItemList | ItemList[], ...args: any[]): BaseItem[] {
    let goods;
    if (Array.isArray(list)) {
      goods = list;
    } else {
      goods = [list];
    }
    const result = [];
    for (let i = 0; i < goods.length; i++) {
      const good: ItemList = goods[i];
      if (!Data.checkLimit(good.type, good.limit, ...args)) continue;
      result.push(...good.items);
    }
    return result;
  }

  /** 获取区间数据 */
  export function getLimitData(list: LimitValue | LimitValue[], ...args: any[]): string[] {
    let value;
    if (Array.isArray(list)) {
      value = list;
    } else {
      value = [list];
    }
    for (let i = 0; i < value.length; i++) {
      const v: LimitValue = value[i];
      if (!Data.checkLimit(v.type, v.limit, ...args)) continue;
      return v.value;
    }
    return [];
  }

  /** 判断范围 */
  export function checkLimit(limitType: LimitType, limit: string, ...args: any[]): boolean {
    switch (limitType) {
      case LimitType.Equla:
        return Number(limit) == args[0];
      case LimitType.NoEqula:
        return Number(limit) != args[0];
      case LimitType.SpaceItem: {
        const ll = limit.split('_');
        for (let i = 0; i < args.length; i++) {
          let boo = true;
          if (args[i] < Number(ll[0]) || args[i] > Number(ll[1])) boo = false;
          if (!boo) return false;
        }
        return true;
      }
      case LimitType.SpaceItemContrary: {
        const ll = limit.split('_');
        for (let i = 0; i < args.length; i++) {
          let boo = true;
          if (args[i] >= Number(ll[0]) && args[i] <= Number(ll[1])) boo = false;
          if (!boo) return false;
        }
        return true;
      }
      case LimitType.SpaceList: {
        const ll = StringUtil.splitStrToNum(limit, ',_');
        for (let i = 0; i < args.length; i++) {
          let boo = false;
          for (let j = 0; j < ll.length; j++) {
            if (args[i] >= ll[j][0] && args[i] <= ll[j][1]) {
              boo = true;
              break;
            }
          }
          if (!boo) return false;
        }
        return true;
      }
      case LimitType.SpaceListContrary: {
        const ll = StringUtil.splitStrToNum(limit, ',_');
        for (let i = 0; i < args.length; i++) {
          let boo = false;
          for (let j = 0; j < ll.length; j++) {
            if (args[i] >= ll[j][0] && args[i] <= ll[j][1]) {
              boo = true;
              break;
            }
          }
          if (boo) return false;
        }
        return true;
      }
      case LimitType.Pass:
        return args[0] > Number(limit);
      case LimitType.PassEqula:
        return args[0] >= Number(limit);
      case LimitType.Less:
        return args[0] < Number(limit);
      case LimitType.LessEqula:
        return args[0] <= Number(limit);
      default:
        return true;
    }
  }

  export function isObject(value: unknown): value is Record<any, any> {
    return value !== null && typeof value === 'object';
  }

  export const hasOwnProperty = Object.prototype.hasOwnProperty;

  /** 比较值是否变化了，考虑NaN；如果是对象直接认为改变了 */
  export function hasChanged(value: any, oldValue: any): boolean {
    if (typeof value === 'object') return true;
    return !Object.is(value, oldValue);
  }

  export function saveData(str: string, value: any) {
    // if (typeof value == 'object') {
    //     const result = Object.fromEntries(value);
    //     sys.localStorage.setItem(str, JSON.stringify(result));
    // } else {
    // }
    sys.localStorage.setItem(str, stringify(value));
  }

  export function copyData<T>(target: T, data: T, ignore?: string[]): T {
    if (target instanceof Map) {
      for (const key in data) {
        if (ignore && ignore.includes(key)) continue;
        let param;
        let num: string | number = Number(key);
        if (isNaN(num)) {
          if (!target.has(key)) continue;
          param = target.get(key);
          num = key;
        } else {
          if (!target.has(num)) continue;
          param = target.get(num);
        }
        if (param instanceof Map) {
          const temp: Map<any, any> = param;
          const keys = Object.keys(data[key]);
          keys.forEach((k) => {
            if (isNaN(Number(k))) {
              temp.set(k, data[key][k]);
            } else {
              temp.set(Number(k), data[key][k]);
            }
          });
        } else {
          target.set(num, data[key]);
        }
      }
    } else {
      for (const key in data) {
        if (ignore && ignore.includes(key)) continue;
        if (target[key] instanceof Map) {
          const temp: Map<any, any> = target[key];
          const keys = Object.keys(data[key]);
          keys.forEach((k) => {
            if (isNaN(Number(k))) {
              temp.set(k, data[key][k]);
            } else {
              temp.set(Number(k), data[key][k]);
            }
          });
        } else {
          target[key] = data[key];
        }
      }
    }
    return target;
  }

  export function stringify(value: any): string {
    let result;
    if (typeof value === 'string') {
      result = `"${value}"`;
    } else if (typeof value === 'number') {
      result = value;
    } else if (value instanceof Map) {
      result = '{';
      if (value.size > 0) {
        value.forEach((v, k) => {
          result += `"${k}":${stringify(v)},`;
        });
        result = result.slice(0, -1);
      }
      result += '}';
    } else if (value instanceof Array) {
      result = '[';
      if (value.length > 0) {
        value.forEach((ele) => {
          result += stringify(ele) + ',';
        });
        result = result.slice(0, -1);
      }
      result += ']';
    } else if (typeof value === 'object') {
      result = '{';
      let i = 0;
      const temp = Object.keys(value);
      temp.forEach((v) => {
        result += `"${v}":${stringify(value[v])},`;
        i++;
      });
      if (i > 0) {
        result = result.slice(0, -1);
      }
      result += '}';
    } else if (value == null) {
      result = '""';
    } else {
      result = value;
    }
    return result;
  }

  export abstract class ProxyObject {
    abstract objName: string;
  }

  export function newDataProxy<T extends ProxyObject>(target: _classExample<T>, ...args): T {
    const data = new target(...args);
    return new Proxy(data, new DataProxyHandler()) as T;
  }

  export function registerDataProxy<T extends ProxyObject>(target: T): T {
    return new Proxy(target, new DataProxyHandler()) as T;
  }

  class DataProxyHandler {
    /**
     *
     * @param target 被代理的对象
     * @param prop 被代理的对象的属性名
     * @param value 被代理的对象的属性值
     * @param receiver 代理对象
     * @returns
     */
    set(target: ProxyObject, prop: string | symbol, value: any, receiver: any): boolean {
      const oldValue = Reflect.get(target, prop, receiver);
      if (Data.hasChanged(value, oldValue)) {
        if (Data.isObject(value)) {
          sys.localStorage(target.objName + '_' + String(prop), JSON.stringify(value));
        } else {
          sys.localStorage(target.objName + '_' + String(prop), value);
        }
        Reflect.set(target, prop, value, receiver);
        return true;
      }
      return false;
    }

    deleteProperty(target: ProxyObject, prop: string | symbol): boolean {
      return Reflect.deleteProperty(target, prop);
    }
  }
}

export const DataMgr = DataManager.Ins();
export const GameData = DataMgr.player;
